package com.way.demo.aop;

import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.way.demo.comm.BusinessException;
import com.way.demo.comm.UserInfo;
import com.way.demo.comm.UserInfoContextHolder;
import com.way.demo.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author lipingwei
 * @create 2021-01-26 13:31
 * 各通知执行顺序：
 *  1:around->
 *  2:before->
 *  3:around->retrun code ->
 *  4:after->
 *  5:afterReturning->
 *  6:afterThrowing
 *  </p>
 *  <p>
 *  order 执行顺序：
 *  1：进入目的方法时：order值越小越先执行
 *  2：从目的方法出去时：order值越大越先执行
 *
 */
//@Aspect
//@Component
//@Order(1)
@Slf4j
public class BaseAop {

    @Autowired
    private TestService testService;

    /**
     * 拦截的包下的规则：严格遵守，写错拦截不到
     * 注意： 1：controller..*.*(..)
     *        包下的任意类..*.*(..) 任意方法，任意参数，任意返回值
     */
    @Pointcut("execution( public * com.way.demo.controller..*.*(..))")
    public void aspect() {
    }

    /** 一：
     * 环绕通知
     * 1：before之前
     * 2：afterReturning之后
     *
     * @apiNote 1：@Around 下接的方法的参数必须为 ProceedingJoinPoint 类型,
     * @apiNote 2：proceedingJoinPoint.proceed() 产生的结果即为  @AfterReturning 中的 result,可 debug 调试
     * @apiNote 3：由于@Around要提前获取到目的方法的执行结果，且@Around提前于@AfterThrowing运行，因此异常将在@Around中被捕获，从而导致@AfterThrowing捕获不到异常，因此@Around与@AfterThrowing混合使用
     **/
    @Around(value = "aspect()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("aop arrounding ：{}", proceedingJoinPoint.getSignature().getName());
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //请求方式：post /get
        String method = request.getMethod();

        //request 只能获取Header中的参数
            String token = request.getHeader("token");
            String source = request.getHeader("source");
            //校验token 与 source
            checkParam(token,source);
            //查询出当前登录用户的信息，存入全局变量threadLocal
            UserInfo userInfo = testService.queryUserById(token, source);
            UserInfoContextHolder.setUserInfo(userInfo);
           //token 信息存入 全局变量 threadLocal
            UserInfoContextHolder.setToken(token);

            //目标方法的 方法名：
            String methodName = proceedingJoinPoint.getSignature().getName();
            //方法入参：body 中的数据
            JSONObject body = getBody(proceedingJoinPoint.getArgs());
            String name = body.getJSONObject("param-0").getString("name");
            Console.log(JSON.toJSONString(body));
        //执行目标方法，返回目标方法的返回值
        Object outObj = proceedingJoinPoint.proceed();
        //对返回数据 处理
        processOutPutObj(outObj);
        Console.log("proceed == {}",JSON.toJSONString(outObj));
        return outObj;
    }

    /**
     *  二：
     * 进入方法体前(进入controller 之前)
     **/
    //@Before(value = "aspect()")
    public void before(JoinPoint joinPoint) {
        log.info("before ：参数类型：{}", joinPoint.getArgs());
    }

    /**
     * 三：
     *
     * 该切面返回数据前（在return之前执行）
     **/
   // @After(value = "aspect()")
    public void after(JoinPoint joinPoint) {
        log.info("aop before return ：{}", joinPoint.toLongString());
    }

    /**
     *
     * 该切面返回数据后
     * joinPoint.getSignature() 返回方法放回类型 及 方法路径
     **/
    @AfterReturning(value = "aspect()", returning = "result")
    public void afterReturning(Object result) {
        log.info("aop after return ：返回结果：{}", result);
    }

    /**
     * 切面报错
     **/
  //  @AfterThrowing(value = "aspect()", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        log.error("exception occured , msg : {}", exception.getMessage());
        if (exception instanceof NullPointerException){
            log.info("空指针异常");
        }
    }

    /**
     * 取得body 中的数据
     */
    private JSONObject getBody(Object[] params){
        JSONObject paramsJson = new JSONObject();
        for (int i = 0; i < params.length; i++) {
            if (params[i] instanceof BindingResult
                    || params[i] instanceof HttpServletRequest
                    || params[i] instanceof HttpServletResponse){
                continue;
            }
          paramsJson.put("param-" + i, JSON.toJSON(params[i]));
           // paramsJson.addProperty("param-" + i, JsonUtil.toJsonWtihNullField(params[i]));
        }
        return paramsJson;
    }

    /**
     * 校验token 与source
     * @param token
     * @param source
     */
    private void checkParam(String token, String source)  {
        if(StrUtil.isBlank(token)){
           throw new BusinessException("token不能为空");
        }
        if(StrUtil.isBlank(source)){
            throw new BusinessException("-1","source不能为空");
        }
    }

//    private void processOutPutObj(Object obj) {
//        System.out.println("OBJ 原本为：" + obj.toString());
//        if (obj instanceof ResultVO) {
             //*****如果为 此对象，则进行强转为此对象******
//            ResultVO resultVO = (ResultVO) obj;
//            resultVO.setMessage("哈哈，我把值修改了" + resultVO.getMessage());
//            System.out.println(resultVO);
//        }
//    }

    /**
     * 处理返回对象
     */
    private void processOutPutObj(Object obj) {
        JSONObject data = JSON.parseObject(JSON.toJSONString(obj)).getJSONObject("data");
        data.put("name","修改返回的值");
    }

}
